package com.situ.tos2024.util;

import com.fasterxml.jackson.annotation.JsonValue;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.invoke.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 功能：基于注解@JsonValue，而无需实现接口
 *
 * @author 千堆雪
 * @version 1.0.0
 * @since 2024/4/9
 * <p>
 * created by 千堆雪 on 2024/4/9 15:54, last modified by 千堆雪 on 2024/4/9 15:54
 */
public class JsonEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
    private final Map<String, E> caches = new HashMap<>();
    private final SilentInvokerHolder.ThrowableFunction<E, Object> valueFunc;

    public JsonEnumTypeHandler(Class<E> type) throws Exception {
        if (type == null) {
            throw new IllegalArgumentException("枚举类型不可为空");
        }

        Method[] methods = type.getDeclaredMethods();
        Field[] fields = type.getDeclaredFields();

        //标注了@JsonValue注解的field
        Field jsonValueField = null;
        for (Field f : fields) {
            if (f.isAnnotationPresent(JsonValue.class)) {
                jsonValueField = f;//以第一个出现的字段为准
                break;
            }
        }

        //标注了@JsonValue注解的method
        Method jsonValueMethod = null;
        for (Method method : methods) {
            //如果方法上标注了@JsonValue注解
            if (method.isAnnotationPresent(JsonValue.class)) {
                jsonValueMethod = method;//以第一个出现的方法为准
                break;
            }
        }

        //方法优先级高于字段，有方法则使用方法，没有方法则使用字段，如果方法和字段都没有使用@JsonValue注解，则使用枚举的默认方法name()
        if (jsonValueMethod != null) {
            valueFunc = jsonValueMethod::invoke;
        } else if (jsonValueField != null) {
            valueFunc = jsonValueField::get;
        } else {
            jsonValueMethod = type.getMethod("name");
            valueFunc = jsonValueMethod::invoke;
        }

        for (E e : type.getEnumConstants()) {
            caches.put(getEnumDesc(e), e);
        }
    }

    //获取枚举描述
    private String getEnumDesc(E e) {
        return SilentInvokerHolder.INVOKER.invoke(valueFunc, e).toString();
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E e, JdbcType jdbcType) throws SQLException {
        if (jdbcType == null) {
            ps.setString(i, getEnumDesc(e));
        } else {
            ps.setObject(i, getEnumDesc(e), jdbcType.TYPE_CODE);
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return caches.get(rs.getString(columnName));
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return caches.get(rs.getString(columnIndex));
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return caches.get(cs.getString(columnIndex));
    }

    private static class SilentInvokerHolder {
        private static final SilentInvoker INVOKER;

        static {
            MethodHandles.Lookup lookup = MethodHandles.lookup();
            try {
                CallSite cs = LambdaMetafactory.metafactory(lookup,
                        //函数式接口的sam方法的名称
                        "invoke",
                        //CallSite的目标方法句柄(getTarget())的方法原型，要注意是否捕获外部变量
                        MethodType.methodType(SilentInvoker.class),
                        //函数式接口的sam方法的原型
                        MethodType.methodType(Object.class, ThrowableFunction.class, Object.class),
                        //真正的实现方法句柄。这个方法句柄有可能是静态方法，有可能是实例方法（虚方法）、构造函数等
                        lookup.findVirtual(ThrowableFunction.class, "apply", MethodType.methodType(Object.class, Object.class)),
                        //在类中动态生成的静态方法（用于将函数式接口方法的调用动态转发到这个静态方法）的原型
                        //注意：如果上面的参数是一个虚方法，则这个原型还是提供要接收的对象
                        MethodType.methodType(Object.class, ThrowableFunction.class, Object.class));
                INVOKER = (SilentInvoker) cs.getTarget().invoke();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }

        //支持抛出异常的函数引用
        @FunctionalInterface
        private interface ThrowableFunction<T, R> {
            R apply(T t) throws Exception;
        }

        //静默调用器
        @FunctionalInterface
        private interface SilentInvoker {
            <T, R> R invoke(ThrowableFunction<T, R> func, T t);
        }
    }
}
